package com.didi.gulimall.seckill.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.didi.gulimall.api.OrderApiService;
import com.didi.gulimall.common.constant.BizCodeEnume;
import com.didi.gulimall.common.constant.OrderConstant;
import com.didi.gulimall.common.constant.SecKillConstant;
import com.didi.gulimall.common.entity.LocalMqEntity;
import com.didi.gulimall.common.exception.DuplicateFieldsException;
import com.didi.gulimall.common.to.MemberResponseVo;
import com.didi.gulimall.common.to.OrderTo;
import com.didi.gulimall.common.to.SkuInfoTO;
import com.didi.gulimall.common.to.mq.SeckillOrderTo;
import com.didi.gulimall.common.utils.R;
import com.didi.gulimall.interceptor.SecKillInterceptor;
import com.didi.gulimall.mq.GuliRabbitTemplate;
import com.didi.gulimall.seckill.service.SecKillService;
import com.didi.gulimall.seckill.to.SecKillSkuRedisTo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.redisson.api.RSemaphore;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @Author 10626
 * @Create 2021/6/14 17:53
 */
@Slf4j
@Service
public class SecKillServiceImpl implements SecKillService {
    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private RedissonClient redissonClient;

    @Autowired
    private GuliRabbitTemplate guliRabbitTemplate;

    @Autowired
    private OrderApiService orderApiService;

    private ThreadLocal<MemberResponseVo> loginUser = SecKillInterceptor.loginUser;

    @Override
    public R getSecKill() {
        Set<String> keys = redisTemplate.keys(SecKillConstant.SESSIONS_CACHE_PREFIX + "*");
        Map<String, List<SkuInfoTO>> map = new HashMap<>();
        if (CollectionUtils.isNotEmpty(keys)) {
            log.info("共有{}场秒杀活动", keys.size());
            for (String key : keys) {
                //seckill:sessions:1582250400000_1582254000000
                String replace = key.replace(SecKillConstant.SESSIONS_CACHE_PREFIX, "");
                String[] split = replace.split("_");
                long startTime = Long.parseLong(split[0]);
                long endTime = Long.parseLong(split[1]);
                Date start = new Date(startTime);
                String format = DateFormatUtils.format(start, "yyyy-MM-dd HH:mm:ss");
                BoundHashOperations<String, String, String> boundHashOps = redisTemplate.boundHashOps(key);
                Set<String> itemKeys = boundHashOps.keys();
                if (CollectionUtils.isNotEmpty(itemKeys)) {
                    List<SkuInfoTO> collect = itemKeys.stream().map(k -> {
                        String json = boundHashOps.get(k);
                        return JSON.parseObject(json, SkuInfoTO.class);
                    }).collect(Collectors.toList());
                    map.put(format, collect);
                }
            }
            log.info("返回数据:{}", JSON.toJSONString(map));
        }
        return R.ok().put("data", map);
    }

    @Override
    public R getSecKillBySkuId(Long skuId) {
        Set<String> keys = redisTemplate.keys(SecKillConstant.SESSIONS_CACHE_PREFIX + "*");
        if (CollectionUtils.isNotEmpty(keys)) {
            log.info("共有{}场秒杀活动", keys.size());
            for (String key : keys) {
                BoundHashOperations<String, String, String> boundHashOps = redisTemplate.boundHashOps(key);
                if (boundHashOps.hasKey(skuId.toString())) {
                    String json = boundHashOps.get(skuId.toString());
                    SecKillSkuRedisTo secKillSkuRedisTo1 = JSON.parseObject(json, SecKillSkuRedisTo.class);
                    log.info("秒杀信息:{}", json);
                    return R.ok().put("data", secKillSkuRedisTo1);
                }
            }
        }
        return R.ok();
    }

    @Override
    public SeckillOrderTo skill(String sessionId, String key, Integer num, Long skuId) {
        long l1 = System.currentTimeMillis();
        MemberResponseVo member = loginUser.get();
        String memberKey = SecKillConstant.SKUKILL_CACHE_PREFIX + member.getId() + ":" + skuId;
        String memberStock = redisTemplate.opsForValue().get(memberKey);
        Integer stock = 0;
        if (StringUtils.isNotEmpty(memberStock)) {
            stock = Integer.parseInt(memberStock);
        }
        String redisKet = SecKillConstant.SESSIONS_CACHE_PREFIX + sessionId;
        if (redisTemplate.hasKey(redisKet)) {
            BoundHashOperations<String, String, String> boundHashOps = redisTemplate.boundHashOps(redisKet);
            String json = boundHashOps.get(skuId.toString());
            if (StringUtils.isBlank(json)) {
                throw new DuplicateFieldsException(BizCodeEnume.NOT_PARTICIPATE_IN_SPIKE.getMsg());
            }
            SkuInfoTO skuInfoTO = JSON.parseObject(json, SkuInfoTO.class);
            Long startTime = skuInfoTO.getStartTime();
            Long endTime = skuInfoTO.getEndTime();
            Long ttl = endTime - startTime;
            long l = System.currentTimeMillis();
            if (!(l >= startTime && l < endTime)) {
                throw new DuplicateFieldsException(BizCodeEnume.SPIKE_OVER.getMsg());
            }
            if (!skuInfoTO.getRandomCode().equals(key)) {
                throw new DuplicateFieldsException(BizCodeEnume.RANDOM_CODE_IS_INCORRECT.getMsg());
            }
            Integer purchaseQuantity = stock + num;
            if (purchaseQuantity > skuInfoTO.getSeckillLimit()) {
                throw new DuplicateFieldsException(BizCodeEnume.EXCEED_PURCHASE_LIMIT.getMsg());
            }
            log.info("校验成功");
            RSemaphore semaphore = redissonClient.getSemaphore(SecKillConstant.SKU_STOCK_SEMAPHORE + key);
            boolean b = semaphore.tryAcquire(num);
            if (b) {
                // 站锁成功
                log.info("{}用户购买{}商品站锁成功", member.getId(), skuId);
                redisTemplate.opsForValue().set(memberKey, purchaseQuantity.toString(), ttl, TimeUnit.MILLISECONDS);
                SeckillOrderTo seckillOrderTo = new SeckillOrderTo();
                seckillOrderTo.setMemberId(member.getId());
                seckillOrderTo.setNum(num);
                seckillOrderTo.setSeckillPrice(skuInfoTO.getSeckillPrice());
                seckillOrderTo.setPromotionSessionId(skuInfoTO.getPromotionSessionId());
                String timeId = IdWorker.getTimeId();
                seckillOrderTo.setOrderSn(timeId);
                seckillOrderTo.setSkuId(skuId);
                seckillOrderTo.setLockStatus(SecKillConstant.LOCKING);
                seckillOrderTo.setRandomCode(skuInfoTO.getRandomCode());
                seckillOrderTo.setStartTime(startTime);
                seckillOrderTo.setEndTime(endTime);
                seckillOrderTo.setSkuInfoTO(skuInfoTO);
                BoundHashOperations<String, String, String> boundHashOpsOrder = redisTemplate.boundHashOps(OrderConstant.ORDER_SESSIONS_CACHE_PREFIX + member.getId());
                boundHashOpsOrder.put(timeId, JSON.toJSONString(seckillOrderTo));
                LocalMqEntity localMqEntity = new LocalMqEntity(UUID.randomUUID().toString(), "seckill-event-exchange", "seckill.create.order", JSON.toJSONString(seckillOrderTo), "com.didi.gulimall.common.to.mq.SeckillOrderTo");
                guliRabbitTemplate.sendConvertAndSend(localMqEntity);
                long l2 = System.currentTimeMillis();
                log.info("共耗时{}ms", l2 - l1);
                return seckillOrderTo;
            } else {
                throw new DuplicateFieldsException(BizCodeEnume.SPIKE_FAILED.getMsg());
            }
        } else {
            throw new DuplicateFieldsException(BizCodeEnume.SPIKE_OVER.getMsg());
        }
    }

    @Override
    public void rollbackOrder(SeckillOrderTo seckillOrderTo) {
        if (seckillOrderTo.getLockStatus().equals(SecKillConstant.UNLOCK)) {
            return;
        }
        R r = orderApiService.getOrderByorderSn(seckillOrderTo.getOrderSn());
        if (r.getCode() != 0) {
            throw new DuplicateFieldsException(r.getMsg());
        }
        OrderTo data = r.getData(new TypeReference<OrderTo>() {
        });
        if (data != null) {
            log.info("订单状态{}", data.getStatus());
            // 增加信号量

            // 积分回滚，
        }
        // 增加信号量
    }

    @Override
    public SeckillOrderTo getSecKillOrder(String orderSn, Long memberId) {
        String key = OrderConstant.ORDER_SESSIONS_CACHE_PREFIX + memberId;
        BoundHashOperations<String, String, String> boundHashOps = redisTemplate.boundHashOps(key);
        String json = boundHashOps.get(orderSn);
        if (StringUtils.isNotEmpty(json)) {
            SeckillOrderTo seckillOrderTo = JSON.parseObject(json, SeckillOrderTo.class);
            return seckillOrderTo;
        }
        return null;
    }

    @Override
    public R getSecKillMemberList() {
        MemberResponseVo memberResponseVo = loginUser.get();
        String key = OrderConstant.ORDER_SESSIONS_CACHE_PREFIX + memberResponseVo.getId();
        BoundHashOperations<String, String, String> boundHashOps = redisTemplate.boundHashOps(key);
        Set<String> keys = boundHashOps.keys();
        if (CollectionUtils.isNotEmpty(keys)) {
            List<SeckillOrderTo> collect = keys.stream().map(s -> {
                String json = boundHashOps.get(s);
                SeckillOrderTo seckillOrderTo = JSON.parseObject(json, SeckillOrderTo.class);
                return seckillOrderTo;
            }).collect(Collectors.toList());
            return R.ok().put("data", collect);
        }
        return R.error(BizCodeEnume.NO_SUCCESSFUL_ORDER.getMsg());
    }
}
